线程锁
线程加锁必须要注意的两点:
- 同步的锁对象可以是任意类型的对象。
- 这些加锁的线程必须使用同一个锁对象。
1. 同步代码块
1 2 3 4 5 6
| 语法格式: synchronized(同步的锁对象){ 需要锁起来的代码:一个线程在运行这段代码期间,不想别的线程半路插入 }
和共享数据相关的语句都要锁起来
|
Thread 的方式创建多线程并加锁
TicketService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| package com.itguigu.ticket;
import java.util.ArrayList;
public class TicketService { ArrayList<String> allTicket;
public TicketService() { allTicket = new ArrayList<>(); allTicket.add("01车01A"); allTicket.add("01车01B"); allTicket.add("01车01C"); allTicket.add("01车01D"); allTicket.add("01车01E"); allTicket.add("01车01F"); allTicket.add("02车01A"); allTicket.add("02车01B"); allTicket.add("02车01C"); allTicket.add("02车01D"); allTicket.add("02车01E"); allTicket.add("02车01F"); }
public boolean hasTicket() { return allTicket.size() > 0; }
public String sellTicket() { return allTicket.remove(0); } }
|
TestThread.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package com.itguigu.ticket;
public class TestThread { public static void main(String[] args) { BuyTicketByThread buyTicketByThread1 = new BuyTicketByThread("窗口1"); BuyTicketByThread buyTicketByThread2 = new BuyTicketByThread("窗口2"); buyTicketByThread1.start(); buyTicketByThread2.start(); } }
class BuyTicketByThread extends Thread{ private static TicketService ticketService = new TicketService(); public BuyTicketByThread(String name) { super(name); }
@Override public void run() { while (true) { synchronized (ticketService) { if (ticketService.hasTicket()) { try { Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " 卖了:" + ticketService.sellTicket()); } catch (InterruptedException e) { e.printStackTrace(); } }else { break; } } } } }
|
Runnable 的方式创建多线程并加锁
TicketService.java(和上面一样)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| package com.itguigu.ticket;
import java.util.ArrayList;
public class TicketService { ArrayList<String> allTicket;
public TicketService() { allTicket = new ArrayList<>(); allTicket.add("01车01A"); allTicket.add("01车01B"); allTicket.add("01车01C"); allTicket.add("01车01D"); allTicket.add("01车01E"); allTicket.add("01车01F"); allTicket.add("02车01A"); allTicket.add("02车01B"); allTicket.add("02车01C"); allTicket.add("02车01D"); allTicket.add("02车01E"); allTicket.add("02车01F"); }
public boolean hasTicket() { return allTicket.size() > 0; }
public String sellTicket() { return allTicket.remove(0); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package com.itguigu.ticket;
public class TestRunnable { public static void main(String[] args) { BuyTicketByRunnable buyTicketByRunnable = new BuyTicketByRunnable(); Thread thread1 = new Thread(buyTicketByRunnable, "窗口1"); Thread thread2 = new Thread(buyTicketByRunnable, "窗口2"); thread1.start(); thread2.start(); } }
class BuyTicketByRunnable implements Runnable{ private TicketService ticketService = new TicketService();
@Override public void run() { while (true) { synchronized (ticketService) { if (ticketService.hasTicket()) { try { Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " 卖了:" + ticketService.sellTicket()); } catch (InterruptedException e) { e.printStackTrace(); } }else { break; } } } } }
|
总结
在使用 synchronized 进行加锁的时候,在 Thread 创建的线程中,需要有一个静态的多个线程多只有一份变量,即上面的 private static TicketService ticketService = new TicketService();
而 Runnable 则不需要此变量为静态,即使用 private TicketService ticketService = new TicketService();
即可,而且 Runnable 还可以将锁对象选定为当前的 this,Thread 则不可以。在其他使用方面,因为是实现的 Runnable 接口,所以线程类还可以继承其他的类,而如果使用 Thread 的话,那就不能继续继承其他的类的,因为 Java 里面只能单继承。所以综上考虑应该优先使用 Runnable。
2. 同步方法
如果一次任务是在一个方法中完成的,那么可以对这个方法直接上锁,即在方法上加上 synchronized 关键字。
1 2
| 语法格式: 【修饰符】synchronized 返回值类型 方法名(【形参列表】)【throws 异常列表】{}
|
同步方法的锁对象:
非静态方法:同步方法的锁对象就是当前的 this 对象
静态方法:当前类的 Class 对象(每一个类型被加载到内存后都会生成一个 Class 对象来表示这个类型,只要是同一种类型,那么 Class 对象就是同一个)
Thread 的方式创建多线程并加锁
在之前同步代码块的基础上将同步代码抽取为一个同步方法,而这里使用的是继承 Thread 的方式创建的线程,所以有多个 BuyTicketByThread,这时我们的同步方法就应该是静态的。因为这样才保证了多个线程使用的是同一个锁对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| package com.itguigu.ticket;
public class TestThread { public static void main(String[] args) { BuyTicketByThread buyTicketByThread1 = new BuyTicketByThread("窗口1"); BuyTicketByThread buyTicketByThread2 = new BuyTicketByThread("窗口2"); buyTicketByThread1.start(); buyTicketByThread2.start(); } }
class BuyTicketByThread extends Thread{ private static TicketService ticketService = new TicketService(); public BuyTicketByThread(String name) { super(name); }
@Override public void run() { while (ticketService.hasTicket()) { saleOneTicket(); } }
public static synchronized void saleOneTicket() { if (ticketService.hasTicket()) { try { Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " 卖了:" + ticketService.sellTicket()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
Runnable 的方式创建多线程并加锁
在之前同步代码块的基础上将同步代码抽取为一个同步方法,所以一个 BuyTicketByRunnable 对象也能启动多个线程。所以这种方式的实现是可以使用 this 对象【BuyTicketByRunnable 对象】作为锁对象的。那么这里就不需要加 static 了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package com.itguigu.ticket;
public class TestRunnable { public static void main(String[] args) { BuyTicketByRunnable buyTicketByRunnable = new BuyTicketByRunnable(); Thread thread1 = new Thread(buyTicketByRunnable, "窗口1"); Thread thread2 = new Thread(buyTicketByRunnable, "窗口2"); thread1.start(); thread2.start(); } }
class BuyTicketByRunnable implements Runnable{ private TicketService ticketService = new TicketService();
@Override public void run() { while (ticketService.hasTicket()) { saleOneTicket(); } }
public synchronized void saleOneTicket() { if (ticketService.hasTicket()) { try { Thread.sleep(10); System.out.println(Thread.currentThread().getName() + " 卖了:" + ticketService.sellTicket()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|